package com.simpleim.util;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadUtil {

    /**
     * CPU数量
     */
    private final static int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    /**
     * 空闲保活时限，单位秒
     */
    private static final int KEEP_ALIVE_SECONDS = 30;

    /**
     * 队列大小
     */
    private static final int QUEUE_SIZE = 10000;

    /**
     * 最大线程数
     */
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT;

    /**
     * IO密集型最大线程
     */
    private static final int IO_MAX = Math.max(2, MAXIMUM_POOL_SIZE);

    /**
     * 混合模型线程数量
     */
    private static final int MIXED_MAX = 128;

    private static final String MIXED_THREAD_AMOUNT = "mixed.thread.amount";

    private static class CustomThreadFactory implements ThreadFactory {

        /**
         * 线程池数量
         */
        private static final AtomicInteger poolNumber = new AtomicInteger(0);

        /**
         * 线程数量
         */
        private static final AtomicInteger threadNumber = new AtomicInteger(0);

        private ThreadGroup threadGroup;

        private String threadTag;

        CustomThreadFactory(String threadTag) {
            SecurityManager manager = System.getSecurityManager();
            threadGroup = manager != null ? manager.getThreadGroup() : Thread.currentThread().getThreadGroup();
            threadTag = "apppool-" + poolNumber.getAndIncrement() + "-" + threadTag + "-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(threadGroup, r, threadTag + threadNumber.getAndIncrement());
            if (thread.isDaemon()) {
                thread.setDaemon(false);
            }
            if (thread.getPriority() != Thread.NORM_PRIORITY) {
                thread.setPriority(Thread.NORM_PRIORITY);
            }
            return thread;
        }
    }

    // CPU密集型线程池
    private static class CpuIntenseTargetThreadPoolLazyHolder {
        public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
                MAXIMUM_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(QUEUE_SIZE),
                new CustomThreadFactory("cpu")
        );

        static {
            // 核心线程也将被应用于超时检测
            EXECUTOR.allowCoreThreadTimeOut(true);
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("CPU密集型任务关闭", new Callable() {
                @Override
                public Object call() throws Exception {
                    shutdownThreadPoolGracefully(EXECUTOR);
                    return null;
                }
            }));
        }
    }

    // 获取CPU密集型线程池
    public static ThreadPoolExecutor getCpuIntenseTargetThreadPool() {
        return CpuIntenseTargetThreadPoolLazyHolder.EXECUTOR;
    }

    private static class IOIntenseTargetThreadPoolLazyHolder {
        public static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
                IO_MAX,
                IO_MAX,
                KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(QUEUE_SIZE),
                new CustomThreadFactory("io")
        );

        static {
            EXECUTOR.allowCoreThreadTimeOut(true);
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("IO密集型任务关闭", new Callable() {
                @Override
                public Object call() throws Exception {
                    shutdownThreadPoolGracefully(EXECUTOR);
                    return null;
                }
            }));
        }
    }

    public static ThreadPoolExecutor getIOIntenseTargetThreadPool() {
        return IOIntenseTargetThreadPoolLazyHolder.EXECUTOR;
    }

    private static class MixedTargetThreadPoolLazyHolder {
        private static final int max = System.getProperty(MIXED_THREAD_AMOUNT) != null ? Integer.parseInt(System.getProperty(MIXED_THREAD_AMOUNT))
                : MIXED_MAX;
        public static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
                max,
                max,
                KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(QUEUE_SIZE),
                new CustomThreadFactory("混合型任务")
        );

        static {
            EXECUTOR.allowCoreThreadTimeOut(true);
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread("混合型任务关闭", new Callable() {
                @Override
                public Object call() throws Exception {
                    shutdownThreadPoolGracefully(EXECUTOR);
                    return null;
                }
            }));
        }
    }

    /**
     * 获取混合型任务
     * @return
     */
    public static ThreadPoolExecutor getMixedThreadPool() {
        return MixedTargetThreadPoolLazyHolder.EXECUTOR;
    }

    static class ShutdownHookThread extends Thread {

        private static volatile boolean hasShudown = false;

        private Callable callable;

        public ShutdownHookThread(String name, Callable callable) {
            super(name);
            this.callable = callable;
        }

        @Override
        public void run() {
            synchronized (this) {
                if (!hasShudown) {
                    hasShudown = true;
                    try {
                        callable.call();
                    } catch (Exception e) {
                        return;
                    }
                }
            }
        }
    }

    /**
     * 优雅关闭线程是
     *
     * @param service
     */
    public static void shutdownThreadPoolGracefully(ExecutorService service) {
        if (!(service instanceof ExecutorService) || service.isTerminated())
            return;
        try {
            // 不在接收新的任务
            service.shutdown();
        } catch (SecurityException e) {
            return;
        } catch (NullPointerException e) {
            return;
        }

        try {
            // 60s没有关闭
            if (!service.awaitTermination(60, TimeUnit.SECONDS)) {
                // 正在运行的任务也要关闭
                service.shutdownNow();

                // 继续等待60s
                if (!service.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.out.println("还有任务在运行，无法关闭");
                }
            }
        } catch (InterruptedException e) {
            // 报错继续关闭正在运行的任务
            service.shutdownNow();
        }

        // 仍然没有关闭
        if (!service.isTerminated()) {
            try {
                // 循环1000次，每次10毫秒继续关闭
                for (int i = 0; i < 1000; i++) {
                    // 如果10毫秒之后关闭，直接break
                    if (service.awaitTermination(10, TimeUnit.MILLISECONDS)) {
                        break;
                    }
                    service.shutdownNow();
                }
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            } catch (Throwable e) {
                System.out.println(e.getMessage());
            }
        }
    }
}
